热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

都会|媒介_[gitbook]Android框架分析系列之AndroidBinder详解

篇首语:本文由编程笔记#小编为大家整理,主要介绍了[gitbook]Android框架分析系列之AndroidBinder详解相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了[gitbook] Android框架分析系列之Android Binder详解相关的知识,希望对你有一定的参考价值。


请支持作者原创:

https://mr-cao.gitbooks.io/android/content/android-binder.html


我这是在gitbook 写笔记的时候发现有位博客写的还不错,在征得博主同意后,把它放在csdn上供大家参考,希望大家多多交流




Android Binder详解


Table of Contents

  • 1. binder简介
  • 2. binder的实现
    • 2.1. IBinder类简介
    • 2.2. IInterface类简介
    • 2.3. BpBinder和BBinder简介
    • 2.4. ProcessState和IPCThreadState简介
    • 2.5. ServiceManager简介
    • 2.6. IMemory简介





本文将对Android的binder机制做一个较为深入的分析,除了讲解binder实现的细节,还会讲解binder通信中的基础原理,以及创建binder service的注意事项。本文的代码分析基于Android4.2.2。






1. binder简介




在我刚刚学习binder的时候,对于binder非常的困惑,现在想起来困惑的原因还是因为对于IPC的不了解。在学习binder之前,最好是对IPC有个基本的了解。IPC是Inter-process communication的缩写,即进程间通信。IPC是一种允许进程间互相通信交换数据的机制。在Linux平台上,进程之间是隔离的,各个进程运行在自己的虚拟地址空间中,如果不采取IPC手段,进程之间是不能互相交换数据的。为了实现进程之间的数据交换,Linux提供了多种IPC机制:




  • 信号

  • 管道

  • Socket

  • 消息队列

  • 信号量

  • 共享内存




Android是基于Linux系统开发,除了上面的IPC机制以外,Android又提供了一种新的选择:binder。本文不打算探究这几种机制之间的差异以及优劣,我的主要关注点在binder的实现上。binder的实现采取了面向对象的编程思想,Android提供了大量的帮助类,通过使用这些帮助类,binder程序开发人员基本不用关心数据是如何在进程之间传递的,而是集中精力设计好顶层服务接口,按照规范实现好proxy和service类就可以很方便的扩展一个本地服务。站在binder开发人员的角度来讲,一个binder的实现包括以下三个方面:




  • 顶层服务接口类的定义,此类中声明了一系列的纯虚函数作为公共的服务接口。类的头文件名一般为IXXXService.h,服务接口类命令为IXXXService,XXX为服务模块名。比如Android系统提供的多媒体服务的接口类为:IMediaPlayerService,其头文件名为IMediaPlayerService.h。

  • proxy端的实现。

  • service端的实现。proxy是相对于service而言,proxy和service都间接继承于IXXXService顶层服务接口类,他们都实现了IXXXService中声明的虚函数接口,所以从外观上看,是没什么区别的。对于用户来说,只需要持有一个IXXXService的指针,就可以调用其服务函数,不用关心这个指针究竟是指向哪个子类的具体实现。proxy和service内部对于同一个函数的实现是有差异的,在proxy端的实现中,是将函数的参数打包进容器,然后透过Android提供的binder通信机制传递给service端,service端的实现是从这个容器中读取出对应的参数,然后调用相应的实现函数。从这个角度来说,proxy只是一个空的壳子,它并没有做实际的工作,而是把做实际工作需要的条件打包好,传递给service,由service来完成具体工作。




下面的图简单的描述了顶层服务接口和proxy代理类,与service服务类之间的关系。IXXXService是一个顶层服务接口类,它声明了一个doAction的方法,其子类proxy和service分别实现了这个方法。但是proxy是将doAction方法参数打包,发送给service,由service负责执行。 




binder驱动是通信的媒介,为通信的进程在驱动层分配buffer,将用户层的参数buffer复制到驱动层buffer,完成数据的交换。下图描述了这个过程: 




站在系统角度来说,binder的实现包括:




  • 一个client进程

  • 一个service进程

  • binder驱动




其中,提供服务接口的进程为service进程,使用服务的为client进程。binder在这两者之间充当通信的媒介,所有的通信数据都是经过bidner传递到对方的用户空间。下面是一个简单的图例,描述了一次同步binder调用的过程: 




proxy调用service的服务,称之为一次binder调用。binder调用有两种形式:




  • 同步调用

  • 异步调用




所谓同步调用就是proxy发送完数据给媒介binder driver之后,开始等待的状态。直到service端处理完本次调用,通过binder driver返回了处理结果。异步调用就简单了,proxy直接发送完数据给媒介binder driver之后就返回了,不用等到service的处理结果。




综上,binder是Android提供的一种IPC通信机制,方便进程之间交换数据。binder的实现包括一个公共的顶层服务接口,同时实现了这个公共顶层接口的proxy代理端和service端。binder driver充当通信媒介。






2. binder的实现




上一个小节中,提纲挈领的介绍了bidner的基础信息,本章主要从代码的角度来分析binder的实现。Android给用户提供了一个共享库:libbinder.so,这个库提供了一系列的接口类和帮助函数,借助这些工具类,我们可以很方便的实现自己的binder service。代码的路径:




frameworks/native/libs/binder




首先我们来看一张”全家福“: 




上面这张图中比较清晰的描述了binder库中提供的类之间的关系。在面向对象编程中,一个对象的行为在它的基类中就定义好了,所以我们首先对上图中两个顶层基类IBinder和IInterface做个简单的介绍,以达到纲举目张的目的。




2.1. IBinder类简介



IBinder是负责binder通信机制的基类,它有两个子类——BpBinder和BBinder。BpBinder代表着proxy,而BBinder代表着service。IBinder中有一个函数:





virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);



这是个纯虚函数,它肩负着binder数据传输的重任,从函数的名字就可以看出其重要性,子类Bpbinder和BBinder负责它的具体实现。BpBinder的此函数负责传输数据,而BBinder的此函数是负责接收数据。关于此函数的参数的解释如下:




  • code:函数码值,每一个服务接口类中声明的虚函数都对应一个码值,proxy端通过将码值传递给service端,从而告知service端请求执行的函数。

  • data:Parcel是Android中的容器类,用于装载数据。每个Parcel都对应一块内存buffer,用于存放数据。data中保存的是执行函数所需要的参数,proxy端把数据打包传递到service端,service端按照顺序读取参数,然后传递给对应的函数去执行。

  • reply:指向Parcel对象的指针,和data不同的是,它是用来装载service执行函数后返回的结果。 proxy可以从此Parcel中读取service的返回值,比如service的函数执行完毕之后返回一个int值,那么就可以调用reply->readInt32()获得这个int值。

  • flags:表示函数是同步调用还是异步调用。默认是同步调用,如果需要异步调用,flags会被赋值为IBinder::FLAG_ONEWAY。同步调用是阻塞的,必须等待service执行完毕返回执行结果之后proxy的执行流才得以继续,否则执行函数调用的线程就一直处于wait状态。




IBinder中有这么两个虚函数:





virtual BBinder* localBinder();
virtual BpBinder* remoteBinder();




默认的实现都是返回NULL。在BBinder中实现了localBinder:





BBinder* BBinder::localBinder()
return this;




在BpBinder中实现了remoteBinder:





BpBinder* BpBinder::remoteBinder()
return this;




所以,如果要区分一个IBinder对象是local binder还是remote binder,那么调用IBinder对象的上述两个函数,对结果进行check就可以知道了。如果localBinder返回非空,那么就是一个local binder,如果remoteBinder返回非空,那么就是一个remote binder。在binder通信中,究竟什么是local binder,什么是remote binder呢?首先,继承自IBinder类的对象,都是binder对象。BBinder因为生存在服务进程中,所以称之为local binder,而BpBinder所对应的实体在另外一个进程中,所以称之为remote binder。BpBinder和BBinder对应关系可以参见下图: 




进程Process_1中有一个BpBinder A,它对应进程Process_2中的BBinder A。相对于Process_1来说,BpBinder A就是remote binder;相对于Process_2来说,BBinder A就是local binder。




同时,进程Process_2中有一个BpBinder B,它对应进程Process_1中的BBinder B。相对于Process_2来说,BpBinder B就是个remote binder,相对于Process_1来说,BBinder B是一个local binder。




从上图我们还可以得知,Process_1调用BpBinder将打包好的函数参数发送到binder driver中Process_2对应的buffer,BBinder A从这块buffer中读取数据,然后进行处理;同理,Process_2调用BpBinder B将打包好的函数参数发送到binder driver中Process_1对应的buffer,BBinder B从这块buffer中读取数据,然后进行处理。一次binder调用从用户空间到kernel空间,数据copy的次数为1。




IBinder类另一个重要的函数:





virtual status_t linkToDeath(const sp& recipient, void* COOKIE = NULL, uint32_t flags = 0) = 0;



这个函数是用来为BBinder注册死亡通知的。在客户端进程中,持有一个BpBinder,它对应着服务进程中的某个BBinder。如果服务进程crash了,那么BBinder也就不存在了,BpBinder就无法再和BBinder通信了。因为BBinder的死亡客户端是没法主动知道的,所以需要注册个死亡通知:当BBinder不存在了,死亡通知就会被派发,以便客户端进程能做一些善后的工作。这个函数只在BpBinder中实现了——很显然,BBinder不需要为自己注册死亡通知。 DeathRecipient是IBinder的一个内部类,它有一个纯虚函数的方法,需要用户自己去实现:





class DeathRecipient : public virtual RefBase
public:
virtual void binderDied(const wp& who) = 0;
;





2.2. IInterface类简介



从抽象的角度来将,基类IBinder实现的是通信数据的传输。这些通信数据来自于顶层服务接口类,所以还需要为服务接口类IXXXService定义一个基类——IInterface。每一个服务接口类IXXXService都需要继承IInterface,IInterface.h中定义了一些和服务相关的变量和函数。 首先看看IInterface的定义:





class IInterface : public virtual RefBase
public:
IInterface();
sp asBinder();
sp<const IBinder> asBinder() const;
protected:
virtual ~IInterface();
virtual IBinder* onAsBinder() &#61; 0;
;




IInterface类相对于IBinder而言&#xff0c;简单了许多。值得关注的是函数asBinder&#xff0c;它返回了一个IBinder的指针。asBinder函数内部实际上是调用的虚函数virtual IBinder* onAsBinder() &#61; 0&#xff0c;由之前的“全家福”可知&#xff0c;这个纯虚函数是由子类BpInterface和BnInterface实现。由此可知&#xff0c;可以从一个IInterfcae获得一个IBinder对象。下面看看两个模板类的不同实现&#xff1a;





template<typename INTERFACE>
IBinder* BnInterface::onAsBinder()
return this;
template<typename INTERFACE>
inline IBinder* BpInterface::onAsBinder()
return remote();




BnInterfce的onAsBinder函数是直接返回的this指针&#xff0c;因为BnInterfce是由IBinder继承而来&#xff1b;BpInterface的onAsBinder函数调用基类BpRefBase的remote函数&#xff0c;返回BpRefBase内部的mRemote指针&#xff0c;这个指针指向的是IBinder对象&#xff0c;后面我们会将到这个对象实际上是一个BpBinder对象。




有没有想过&#xff0c;为什么需要从一个IInterfce类中获得一个IBinder对象&#xff1f;答案很简单&#xff0c;只有IBinder对象是可以跨进程传递的。如果我们要把一个IBinder对象从一个进程传递到另外一个进程&#xff0c;那么就首先要获得它的IBinder指针。binder驱动不仅仅支持常规的数据类型传递&#xff0c;具有特色的是还支持binder对象和文件句柄的传递。传输一个IBinder对象&#xff0c;也分两种情况&#xff0c;一种是传输的是BBinder&#xff0c;另一种传输的是BpBinder。这两种情况还是有差异的&#xff0c;后面我们也会讲到。




IInterface.h文件中还定义了两个非常重要的宏&#xff1a;





#define DECLARE_META_INTERFACE(INTERFACE) \\
static const android::String16 descriptor; \\
static android::sp asInterface( \\
const android::sp& obj); \\
virtual const android::String16& getInterfaceDescriptor() const; \\
I##INTERFACE(); \\
virtual ~I##INTERFACE(); \\

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \\
const android::String16 I##INTERFACE::descriptor(NAME); \\
const android::String16& \\
I##INTERFACE::getInterfaceDescriptor() const \\
return I##INTERFACE::descriptor; \\
\\
android::sp I##INTERFACE::asInterface( \\
const android::sp& obj) \\
\\
android::sp intr; \\
if (obj !&#61; NULL) \\
intr &#61; static_cast( \\
obj->queryLocalInterface( \\
I##INTERFACE::descriptor).get()); \\
if (intr &#61;&#61; NULL) \\
intr &#61; new Bp##INTERFACE(obj); \\
\\
\\
return intr; \\
\\
I##INTERFACE::I##INTERFACE() \\
I##INTERFACE::~I##INTERFACE() \\




当我第一次阅读binder代码&#xff0c;看到上面这两个宏的时候&#xff0c;确实是“懵”了。其实上面代码的逻辑还是很简单的&#xff0c;只是因为使用了宏定义所以显的比较复杂而已。DECLARE_META_INTERFACE就干了四件事情&#xff1a;




  • 为IInterface的子类IXXXXService声明了一个描述符&#xff0c;这个描述符用来唯一描述IXXXService。同时这个描述符在binder通信过程中&#xff0c;还充当了”校验码“码的功能&#xff0c;当service端收到数据后&#xff0c;首先就要从数据中读取一个描述符&#xff0c;验证描述符是否匹配自身的描述符。如果是&#xff0c;证明数据有效&#xff0c;否则就是个非法的数据包&#xff0c;不予处理。

  • 声明了函数asInterface&#xff0c;从一个IBinder对象获得一个IXXXService对象。

  • 声明函数getInterfaceDesCriptor用来获取声明的字符串描述符。

  • 声明IXXXService的构造函数和析构函数。




IMPLEMENT_META_INTERFACE宏展开之后&#xff0c;就是对DECLARE_META_INTERFACE宏声明的变量的定义和函数的实现了。我们只关注asInterface这个函数的实现&#xff0c;这里我们以Android多媒体服务的接口类IMediaPlayerService为例替换INTERFACE为MediaPlayerService&#xff0c;来看看这个函数的实现&#xff1a;





android::sp IMediaPlayerService::asInterface(const android::sp& obj)
android::sp intr;
if (obj !&#61; NULL)
intr &#61; static_cast(
obj->queryLocalInterface(IMediaPlayerService::descriptor).get());
if (intr &#61;&#61; NULL)
intr &#61; new BpMediaPlayerService(obj);


return intr;




既然提到了MediaPlayerService&#xff0c;那就上一张IMediaPlayerService的类图&#xff0c;这样也方便接下来的代码分析&#xff1a;




IMediaPlayerService::asInterface这个函数首先会调用IBinder的queryLocalInterface函数检查IBinder的子类是否是一个本地service接口。在IBinder中提供了默认的实现&#xff1a;





sp IBinder::queryLocalInterface(const String16& descriptor)
return NULL;




IBinder有两个子类&#xff1a;BpBinder和BBinder&#xff0c;BpBinder并没有对这个函数进行了重写&#xff0c;沿用了基类的默认实现方式。在BBinder的子类BnInterface中对这个函数进行了重写&#xff0c;在函数实现体中&#xff0c;如果检查传递的描述符字符串和自身的描述符相同&#xff0c;就返回this指针。





template<typename INTERFACE>
inline sp BnInterface::queryLocalInterface(
const String16& _descriptor)
if (_descriptor &#61;&#61; INTERFACE::descriptor) return this;
return NULL;




所以对于IMediaPlayerService::asInterface(const android::sp& obj)函数&#xff0c;如果形参IBinder引用obj指向的是一个BpBinder对象&#xff0c;那么obj→queryLocalInterface函数就返回NULL&#xff0c;需要以obj为参数构造一个BpMediaPlayerService对象&#xff08;继承自IMediaPlayerService&#xff09;;如果形参IBinder引用obj指向的是一个BBinder对象&#xff0c;返回的就是this指针。因为BnInterface作为BBinder的子类的同时也是IMediaPlayerService的子类。




DECLARE_META_INTERFACE费劲心思声明的asInterface函数的作用是非常重要的&#xff0c;它可以直接从IBinder构造一个IMediaPlayerService对象&#xff0c;从而屏蔽了这个对象究竟是本地service对象&#xff0c;还是一个remote代理对象。用户直接使用IMediaPlayerService接口的指针就可以调用具体的函数实现&#xff0c;丝毫不用关心底层的实现细节。




DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE宏要结合起来使用&#xff0c;DECLARE_META_INTERFACE在IXXXService.h的类定义体中使用&#xff1b;而IMPLEMENT_META_INTERFACE宏在IXXXService.cpp中使用。




IInterface.h中还定义了一个帮助函数&#xff0c;封装了上边讲到的asInterface。





template<typename INTERFACE>
inline sp interface_cast(const sp& obj)
return INTERFACE::asInterface(obj);




比如调用interface_cast(obj)就可以获得一个IMediaPlayerService指针。这里留一个悬念&#xff1a;interface_cast形参obj从哪里获得的呢&#xff1f;之后的章节会讲到。





2.3. BpBinder和BBinder简介



之前的章节已经简单的介绍了BpBinder和BBinder&#xff0c;我们已经知道BpBinder负责传输数据&#xff0c;BBinder负责接收数据和处理数据。这一小节&#xff0c;会深入的分析BpBinder和BBinder的关系。




在BpBinder类的内部有一个成员变量mHandle&#xff0c;它是一个int型变量。这个变量所代表的含义是什么呢&#xff1f;对于每一个经过binder driver传输的BBinder对象&#xff0c;binder driver都会在驱动层为它构建一个binder_node数据结构&#xff1b;同时为这个binder_node生成一个“引用”&#xff1a;binder_ref&#xff0c;每一个binder_ref都有一个int型的描述符。BpBinder的成员变量mHandle的值就是bidner_ref中的int型描述符&#xff0c;这样就建立起了用户层的Bpbinder和一个驱动空间的binder_ref数据结构的对应关系。通过 “BpBinder handle→binder_ref→binder_node→BBinder”这样的匹配关系&#xff0c;就可以建立一个Bpbinder对应一个BBinder的关系。 下面这张图描述了Handle和binder_ref以及binder_node的关系&#xff1a; 




一个binder_node可能有很多个binder_ref引用它&#xff0c;但是一个客户进程内&#xff0c;对于同一个binder_node的引用只会存在一份——也就是说。也就是说&#xff0c;如果进程A中有一个BBinder service&#xff0c;进程B持有多个BpXXXService的代理类&#xff0c;但是这些代理类都对应同一个BpBinder对象。这点其实在我们的“全家福”里面已经体现的很清楚了。BpRefBase和BpBinder的关系不是继承关系&#xff0c;而是一个聚合关系。这点其实很好理解&#xff0c;对同一个BBinder维持多个BpBinder是一件很浪费空间的事情。另外&#xff0c;BpBinder的数目也影响着BBinder的生命周期&#xff0c;同一个BBinder使用同一个BpBinder也简化了生命周期的管理。




上面我们讲到了通过binder的传输的BBinder&#xff0c;驱动都会为之建立一个binder_node数据结构。那么一个BBinder是如何由一个进程传递到另一个进程&#xff0c;进而得到一个匹配的BpBinder的呢&#xff1f;这就不得不讲到Parcel了。




2.3.1. binder传输容器Parcel



Parcel是Android提供的一个容器类&#xff0c;通过binder传输的数据都可以写进Parcel中。但是因为Parcel中的数据最终都会copy到驱动空间&#xff0c;而驱动空间为每一个进程分配的内存是有效的&#xff0c;如果Parcel中的数据量太大就会造成数据传输失败。所以&#xff0c;binder通信中不适宜用Parcel传输诸如图片这样的数据&#xff0c;如果有需求传输大容量数据&#xff0c;可以使用共享内存IMemory。这个我们会在本文的最后讲解。




Parcel中不仅可以装载int&#xff0c;float&#xff0c;string这样的基础数据类型&#xff0c;还可以装载IBinder对象&#xff0c;文件句柄等特俗对象。现在我们就来看看Parcel是如何装载IBinder对象。首先看一张图&#xff1a; 




对图中几个数据结构做下解释&#xff1a;




  • binder_write_read:用户层传递数据给binder驱动使用的是ioctl的方式&#xff08;ioctl&#xff08;fd&#xff0c;cmd&#xff0c;arg&#xff09;&#xff09;&#xff0c;当cmd为BINDER_WRITE_READ的时候&#xff0c;arg对应的就是binder_write_read结构体。除了binder_write_read以外还有几个其他的命令&#xff0c;但是用的最频繁的就是binder_write_read。

  • binder_transaction_data:BINDER_WRITE_READ命令的数据也是以&#xff1a;命令&#43;数据的格式存放。当cmd为BC_TRANSACTION的时候&#xff0c;对应的数据结构是binder_transaction_data。

  • flat_binder_object&#xff1a;Parcel中存放的binder对象&#xff0c;以此数据结构来表征。




每一个Parcel都对应一块内存buffer&#xff0c;Parcel内部有三个重要的变量&#xff1a;mData,mObjects,mObjectsSize。




  • mData:指向数据buffer&#xff0c;调用write接口写入的数据都依次存放在这块buffer中。

  • mObjects:指向一个动态分配的一维数组&#xff0c;这个数组的大小可以动态的扩展。里面存放的是mData的下标值。当数据buffer中写入了binder对象&#xff0c;就会在mObjects中存放一条下标记录&#xff0c;表示在数据buffer的某个地方存放的是一个binder对象。

  • mObjectsSize&#xff1a;mObjects数组的大小。




上图中的Parcel buffer中写入了一个IXXXService的描述符&#xff0c;接着是正常的参数参数&#xff0c;比如int之类的数据&#xff0c;紧接着写入了三个binder对象。第一个是BBinder&#xff0c;第二个是BpBinder&#xff0c;第三个是一个文件句柄。他们共同以flat_binder_object数据结构来表示&#xff0c;只是数据结构中的值不一样。当我们要传递一个IBinder到另外一个进程的时候&#xff0c;先要调用Parcel的writeStrongBinder函数将此IBinder对象写入到Parcel中&#xff1a;





status_t Parcel::writeStrongBinder(const sp& val)
return flatten_binder(ProcessState::self(), val, this);
status_t flatten_binder(const sp& proc,
const sp& binder, Parcel* out)
flat_binder_object obj;
obj.flags &#61; 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder !&#61; NULL)
IBinder *local &#61; binder->localBinder();
if (!local)
BpBinder *proxy &#61; binder->remoteBinder();
if (proxy &#61;&#61; NULL)
ALOGE("null proxy");

const int32_t handle &#61; proxy ? proxy->handle() : 0;
obj.type &#61; BINDER_TYPE_HANDLE;
obj.handle &#61; handle;
obj.COOKIE &#61; NULL;
else
obj.type &#61; BINDER_TYPE_BINDER;
obj.binder &#61; local->getWeakRefs();
obj.COOKIE &#61; local;

else
obj.type &#61; BINDER_TYPE_BINDER;
obj.binder &#61; NULL;
obj.COOKIE &#61; NULL;

return finish_flatten_binder(binder, obj, out);




因为IBinder的引用可能是指向BpBinder&#xff0c;也可能指向BBinder&#xff0c;所以在上述的代码中首先是通过IBinder的localBinder试图获取一个本地binder对象&#xff0c;如果不为空&#xff0c;则表示传递的是一个BBinder对象。如果为空则表示传递的是一个BpBinder。如果传递的是一个BBinder&#xff0c;则需要在驱动层生成binder_ref数据结构&#xff0c;同时把BBinder在用户空间所对应的地址保存在驱动层中。




在通过BpXXXService调用函数接口的时候&#xff0c;实际上是把参数写入到Parcel中&#xff0c;然后由Parcel构造一个bidner_transaction_data数据结构。然后将BC_TRANSACTION命令和binder_transaction_data写入Parcel&#xff0c;在由此Parcel构造最终的binder_write_read数据结构&#xff0c;通过ioctl将此数据结构传递给驱动。





2.3.2. binder的传输



从上小节的图中可以得知&#xff0c;IBinder对象是以flat_binder_object结构体在binder驱动中传递。binder驱动通过将用户空间的Parcel所对应的buffer和mObjects所对应的buffer拷贝到目标进程的驱动内存空间&#xff0c;然后通过mObjects数组的偏移量在数据buffer中找到对应的flat_binder_object对象&#xff0c;根据type的不同创建binder_noede或者是binder_ref数据结构予以描述binder对象。




对于BBinder来说&#xff0c;驱动会为之创建一个binder_node数据结构&#xff0c;同时创建的还有binder_ref数据结构。当传递BBinder的时候&#xff0c;flat_binder_object的type初始值是BINDER_TYPD_BINDER,当binder驱动将此数据结构拷贝到目标进程的内存空间的时候&#xff0c;将type替换为binder_type_handle&#xff0c;并且把handle赋值为binder_ref的int描述符。




用户空间通过从Parcel中读取出对应的flat_binder_object数据结构&#xff0c;然后创建一个BpBinder对象。





sp Parcel::readStrongBinder() const sp val; unflatten_binder(ProcessState::self(), *this, &val); return val;
status_t unflatten_binder(const sp& proc,
const Parcel& in, sp* out)
const flat_binder_object* flat &#61; in.readObject(false);
if (flat)
switch (flat->type)
case BINDER_TYPE_BINDER:
*out &#61; static_cast(flat->COOKIE);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out &#61; proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast(out->get()), *flat, in);


return BAD_TYPE;
sp ProcessState::getStrongProxyForHandle(int32_t handle)
sp result;
AutoMutex _l(mLock);
handle_entry* e &#61; lookupHandleLocked(handle);
if (e !&#61; NULL)
// We need to create a new BpBinder if there isn&#39;t currently one, OR we
// are unable to acquire a weak reference on this current one. See comment
// in getWeakProxyForHandle() for more info about this.
IBinder* b &#61; e->binder;
if (b &#61;&#61; NULL || !e->refs->attemptIncWeak(this))
b &#61; new BpBinder(handle);
e->binder &#61; b;
if (b) e->refs &#61; b->getWeakRefs();
result &#61; b;
else
// This little bit of nastyness is to allow us to add a primary
// reference to the remote proxy when this team doesn&#39;t have one
// but another team is sending the handle to us.
result.force_set(b);
e->refs->decWeak(this);


return result;




  • 驱动通过将数据buffer 拷贝到目标进程的内存空间&#xff0c;用户空间以此buffer构造一个Parcel对象。所以才可以通过readStrongBinder来获取其他进程写入的binder对象。

  • unflatten_binder&#xff1a;当type是BINDER_TYPE_HANDLE的时候&#xff0c;以handle为参数调用ProcessState::getStrongProxyForHandle苟赞一个BpBinder。

  • ProcessState::getStrongProxyForHandle函数首先会检查此handle所对应的BpBinder是否已经创建过&#xff0c;如果已经创建&#xff0c;则返回保存的BpBinder指针&#xff1b;如果没有创建就重新构造一份BpBinder&#xff0c;并且保存在Vector中。因为在进程内ProcessState是单例的&#xff0c;所以对于同一个handle&#xff0c;整个系统只会存在一个BpBinder。




由此就完成了BBinder的传输&#xff1a;发送进程的BBinder&#xff0c;到了目标进程之后&#xff0c;将会得到一个BpBinder。同时驱动也会创建对应的数据结构&#xff1a;binder_node和binder_ref。





2.3.3. BBinder生命周期



Android对于c&#43;&#43; 对象的生命周期的管理是通过sp指针来管理的。一般对象的生命周期只受此对象的强引用计数的影响。当对象的强引用计数为0的时候&#xff0c;就会调用对象的析构函数释放对象的内存。




当我们通过要将一个BBinder传递到驱动的时候&#xff0c;首先要把这个BBinder对象写入到Parcel。Parcel提供了两个函数才管理BBinder对象的引用计数&#xff1a;





void acquire_object(const sp& proc, const flat_binder_object& obj, const void* who);
void release_object(const sp& proc, const flat_binder_object& obj, const void* who);




当写入BBinder到一个Parcel的时候&#xff0c;会调用acquire_object来增加BBinder的强引用计数&#xff0c;这样确保在写入驱动的过程中&#xff0c;用户空间的BBinder不会被析构。




当用户层和驱动的一次会话结束的时候&#xff0c;Parcel容器会被析构&#xff0c;在Parcel的析构函数中会调用release_object将对象强引用计数减1&#xff0c;然后释放掉自己所分配的buffer。




在驱动层检测到用户传输下来的数据中有BBinder对象&#xff0c;会使用命令BR_INCREFS和BR_ACQUIRE通知用户空间增加BBinder对象的弱引用计数和强引用计数。在驱动层binder_node保存着BBinder对象在用户空间的地址以及管理其在驱动层的计数关系。binder_ref相当于对binder_node引用&#xff0c;每增加一个binder_ref&#xff0c;binder_node的internal_strong_refs变量都会加1.每减少一个binder_ref的时候&#xff0c;binder_node的internal_strong_refs都会减1.当此变量为0的时候&#xff0c;就会以BR_RELEASE和BR_DECREFS通知BBinder对象&#xff0c;减少在用户空间的计数&#xff0c;如果BBinder的强引用计数减少为0就会导致BBinder的析构函数的执行。binder_ref的存在受BpBinder的影响&#xff0c;一个BpBinder对应着唯一的一个binder_ref&#xff0c;当BpBinder在用户空间的强引用计数变为0 的时候&#xff0c;binder_ref的引用计数也会变为0&#xff0c;导致binder_ref被删除。




关于binder_node和binder_ref的引用计数关系是可以从sys系统中查看&#xff1a;





cat sys/kernel/debug/binder/proc/PID



这里以一张传递BBinder过程中&#xff0c;binder_node&#xff0c;binder_ref计数变化的时序图结束本章的讲解&#xff1a; 






2.4. ProcessState和IPCThreadState简介



在每一个支持binder通信的进程的代码中&#xff0c;必然会有这么一句代码&#xff1a;





sp proc(ProcessState::self());



上述代码创建了一个单例对象&#xff1a;ProcessState。这个对象在自己的构造函数中打开binder驱动文件&#xff1a;/dev/binder&#xff0c;获得一个文件句柄。以后和驱动的交互都是通过ioctl操作这个文件句柄来完成。同时在构造函数中&#xff0c;还使用mmap建立一块和驱动共享的内存。正是因为每个进程和驱动都有一块共享的内存&#xff0c;所以binder通信的数据拷贝次数才会为1.




此外ProcessState还有两个重要的任务&#xff1a;




  • 保存着全进程内所有的BpBinder对象

  • 扩展binder线程




一般情况下&#xff0c;我们在进程的入口函数main中&#xff0c;只会开辟两个线程用于binder通信&#xff0c;当服务进程同时处理多个请求的时候&#xff0c;binder线程可能会不够用&#xff0c;这个时候binder驱动会通知进程扩展binder线程。扩展线程的工作是由函数&#xff1a; void ProcessState::spawnPooledThread(bool isMain)来完成。




如果说&#xff0c;ProcessState是代表着binder通信的进程&#xff0c;那么IPCThreadState就代表着binder通信的线程。每一个使用binder通信机制的线程&#xff0c;都有一个线程特定的变量&#xff1a;IPCThreadState。这个对象用于和驱动的直接交互&#xff0c;整个binder通信协议的实现都是在这个类中。




IPCThreadState和驱动的通信使用ioctl&#xff08;fd&#xff0c;cmd&#xff0c;data&#xff09; 其中cmd有&#xff1a;





#define BINDER_WRITE_READ _IOWR(&#39;b&#39;, 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT _IOW(&#39;b&#39;, 3, int64_t)
#define BINDER_SET_MAX_THREADS _IOW(&#39;b&#39;, 5, size_t)
#define BINDER_SET_IDLE_PRIORITY _IOW(&#39;b&#39;, 6, int)
#define BINDER_SET_CONTEXT_MGR _IOW(&#39;b&#39;, 7, int)
#define BINDER_THREAD_EXIT _IOW(&#39;b&#39;, 8, int)
#define BINDER_VERSION _IOWR(&#39;b&#39;, 9, struct binder_version)




平时客户端调用服务端的接口&#xff0c;使用的通信协议命令就是BINDER_WRITE_READ。有上述定义可知&#xff0c;每条命名都对应了不同的数据结构。每条通信命令 都对应一个唯一的整数&#xff0c;这个32位的整数的含义如下&#xff1a; 




以_IOW为例&#xff0c;其第一个参数对应着type&#xff0c;第二个参数对应这nr&#xff0c;第三个参数对应着size。其中_IOX,X可以是W或者是R&#xff0c;代表着数据传输的方向&#xff0c;W为1&#xff0c;R为2.




以上的每一条命令对应的数据包又是以cmd&#43;数据的方式存放。具体的可以参考binder.h中的定义。





2.5. ServiceManager简介



每个binder service都应该有一个“入口地址”&#xff0c;这个“入口地址”&#xff0c;也就是handle句柄是由ServiceManager管理的。所有的客户端&#xff0c;如果想要调用binder service的服务&#xff0c;必然要先从SerivceManager处获得binder service的handle句柄&#xff0c;然后构造一个BpBinder,再接着以此BpBinder构造BpXXXService。而客户端和ServiceManager之间的通信也是透过binder进行&#xff0c;客户端必要也要知道ServiceManager的handle句柄&#xff0c;为了方便&#xff0c;Android规定ServiceManager的handle为0。





sp defaultServiceManager()
if (gDefaultServiceManager !&#61; NULL) return gDefaultServiceManager;

AutoMutex _l(gDefaultServiceManagerLock);
if (gDefaultServiceManager &#61;&#61; NULL)
gDefaultServiceManager &#61; interface_cast(
ProcessState::self()->getContextObject(NULL));


return gDefaultServiceManager;
sp ProcessState::getContextObject(const sp& caller)
return getStrongProxyForHandle(0);




上述代码描述的就是IServiceManager创建的过程。通过指定handle为0&#xff0c;获得一个IBinder&#xff08;其实是IBinder的子类BpBinder&#xff09;对象&#xff0c;然后构造出一个IServiceManager对象&#xff08;其实是其子类BpServiceManager&#xff09;。用户通过IServiceManager对象来查询以及注册binder service。




下面有一张图&#xff0c;描述了ServiceManager和Client&#xff0c;Service之间的关系。 




  • Service进程调用addService函数来注册一个IBinder&#xff08;BBinder&#xff09;&#xff0c;ServiceManager中保存着IBinder&#xff08;BBinder&#xff09;对应的handle句柄

  • Client进程通过getService函数来获得一个BBinder对应的句柄&#xff0c;构造出代理对象。




是不是所有的进程都可以注册binder service呢&#xff1f;也不是。ServiceManager中对于注册service的权限是有限制的。





static struct
unsigned uid;
const char *name;
allowed[] &#61;
AID_MEDIA, "media.audio_flingIMemoryer" ,
AID_MEDIA, "media.player" ,
AID_MEDIA, "media.camera" ,
AID_MEDIA, "media.audio_policy" ,
AID_DRM, "drm.drmManager" ,
AID_NFC, "nfc" ,
AID_BLUETOOTH, "bluetooth" ,
AID_RADIO, "radio.phone" ,
AID_RADIO, "radio.sms" ,
AID_RADIO, "radio.phonesubinfo" ,
AID_RADIO, "radio.simphonebook" ,
AID_RADIO, "phone" ,
AID_RADIO, "sip" ,
AID_RADIO, "isms" ,
AID_RADIO, "iphonesubinfo" ,
AID_RADIO, "simphonebook" ,
AID_MEDIA, "common_time.clock" ,
AID_MEDIA, "common_time.config" ,
;
int svc_can_register(unsigned uid, uint16_t *name)

unsigned n;
if ((uid &#61;&#61; 0) || (uid &#61;&#61; AID_SYSTEM))
return 1;
for (n &#61; 0; n <sizeof(allowed) / sizeof(allowed[0]); n&#43;&#43;)
if ((uid &#61;&#61; allowed[n].uid) && str16eq(name, allowed[n].name))
return 1;
return 0;




ServiceManager在接收到Serivce进程的注册请求之后&#xff0c;会对Service进程的uid进程检查。检查规则如下&#xff1a;




  • 如果Service进程的uid为root&#xff0c;或者AID_SYSTEM,可以注册&#xff1b;

  • 如果uid在allowed数组中&#xff0c;并且注册的binder service的名字也和allowed数组中的字符串匹配&#xff0c;就允许注册&#xff1b;

  • 除上述两种情况以外&#xff0c;都不允许注册




也就是说&#xff0c;只有指定的uid才是可以注册binder service。





2.6. IMemory简介



因为binder驱动为每个参与通信的进程只分配了总数为1M的共享内存空间&#xff0c;所以在传输大容量的数据的时候&#xff0c;不能直接数据写入Parcel传递给binder驱动。




如果我们需要传递大容量数据&#xff0c;比如一张图片&#xff0c;这个时候就需要使用IMemory类了。IMemory象征着一块共享内存&#xff0c;用于在两个进程之间交换数据&#xff0c;我们先来看一个例子。





sp heap &#61; new MemoryHeapBase(size, 0, "MetadataRetrieverClient");
sp mAlbumArt &#61; new MemoryBase(heap, 0, size);
memcpy(mAlbumArt->pointer(), albumArtata, albumArtmSize);




上面三行代码表示了IMemory最简单的用法&#xff1a;




  • MemoryHeapBase:代表一块堆内存。通过构造MemoryHeapBase&#xff0c;获得一份共享内存。其构造函数第一个参数size&#xff0c;指定了创建内存的大小&#xff1b;第二个变量是flag标记&#xff0c;指定为0即可&#xff1b;第三个是这个共享内存的名字。

  • MemoryBase&#xff1a;代表上述分配的内存中的一个小块内存。在需要创建多份共享内存的时候&#xff0c;为了避免创建MemoryHeapBase带来的开销&#xff0c;一般是创建一份MemoryHeapBase&#xff0c;然后将其分为多个小的MemoryBase。

  • 获得一个IMemory对象之后&#xff0c;就可以同通过其pointer()方法获得其指针地址&#xff0c;然后往这个指针所指向的地址写入数据。




在写入数据完毕之后&#xff0c;可以把这个IMemory写入到Parcel传递到client进程。client就可以采取相反的动作&#xff0c;从这个IMemory中读取数据。




为什么IMemory可以跨进程传递&#xff1f;因为它是基于binder实现&#xff0c;下面是它的类图关系&#xff1a; 




从图中可以发现&#xff0c;绿色部分标记的类IMemory和IMemoryHeap是顶层接口类。其中IMemoryHeap代表分配的大内存&#xff0c;而IMemory代表在IMemoryHeap上分配的一小块内存。IMemoryHeap的子类MemoryHeapBase负责实现内存的分配。




IMemory和IMemoryHeap的关系可以用下图来描述&#xff1a; 




在客户端&#xff0c;就可以通过以下代码获取一个IMemory和这块内存在本进程所对应的地址和大小&#xff1a;





sp artWorkMemory &#61; interface_cast&#xff08;ibinder&#xff09;;
void *data &#61; artWorkMemory->pointer();
int size &#61; artWorkMemory->size();




其实关于IMemory的使用还是很简单的&#xff0c;第一步创建一个MemoryHeapBase,负责创建整块内存&#xff1b;第二步创建一个MemoryBase&#xff0c;从整块内存中分配一小块内存&#xff1b;第三步使用pointer()方法获取到MemoryBase所对应的地址&#xff0c;往地址中写入数据&#xff1b;第四步将MemoryBase的基类IBinder写入到Parcel中传递到客户端&#xff1b;第五步客户端从Parcel中读取一个IBinder&#xff0c;构造一个IMemory对象&#xff0c;同样调用pointer()方法获得内存地址&#xff0c;以及调用size()大小获得内存的大小。 \\ 如果向创建多个IMemory&#xff0c;那就设计到如何有效的从一整块内存中分配小内存的问题了&#xff0c;必然要引入一个内存管理器&#xff0c;好在Android已经帮我们写好了帮助内。下面的代码是一个试例&#xff1a;





sp memoryDealer &#61; new MemoryDealer(length1&#43;length2, "ArtWork");
sp artwokMemory &#61; memoryDealer->allocate(length1);
sp artwokMemory &#61; memoryDealer->allocate(length2);




这段代码和最初的代码不同之处在于IMemory的分配不在由我们手动分配&#xff0c;而是使用帮助类MemoryDealer来处理。下面是MemoryDealer的构造函数&#xff1a;





MemoryDealer::MemoryDealer(size_t size, const char* name)
: mHeap(new MemoryHeapBase(size, 0, name)),
mAllocator(new SimpleBestFitAllocator(size))




推荐阅读
  • 软件自动化测试的学习路线
    软件自动化测试的学习步骤软件测试交流群关注软件测试技术公众号获取阅读目录软件自动化测试的学习步骤自动化测试的本质自动化测试学习的误区自动化测试的职位自动化测试分类Web自动化 ... [详细]
  • 前端微服务二
    为了解决庞大的一整块后端服务带来的变更与扩展方面的限制,出现了微服务架构(Microservices):微服务是面向服务架构(SOA)的一种变体,把应用程序设计成一系列松耦合的细粒 ... [详细]
  • mongodb修改bindIp和启动关闭
    安装参考:https:blog.csdn.netzombresarticledetails82385955先关闭:sudoservicemongodstop再修改etcmongod ... [详细]
  • 淘宝http:ip.taobao.cominstructions.php接口说明请求接口(GET):servicegetIpInfo.ph ... [详细]
  • SSL协议、TLS协议,使用哪一种更安全?
    在金融银行业,保护机密信息的安全至关重要。由于财务记录完全通过在线数据库维护,因此实施保护客户、银行和金融机构免受黑客攻击的安全功能比以往任何时候都更加重要。安全套接字层(SSL) ... [详细]
  • WebBrowser控件(1)
    WindowsPhone7内置了一个强大的网络浏览器,该浏览器的内核是基于桌面版的InternetExplorer7(Mango版基于InternetE ... [详细]
  • nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 这两天做了一个小项目,里面有个下载进度的进度条需要制作。先看呈现的效果:点击进度,然后依次递增,直到递增到百分之百。现在把这部分代码分享下来。<!DOCTYPEhtml><html ... [详细]
  • 1、概念共享内存:共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同malloc()函数向不同进程返回了指向同一个 ... [详细]
  • Android JNI学习之Concepts
    2019独角兽企业重金招聘Python工程师标准ConceptsBeforeBeginningThisguideassumesthatyouare:Alreadyfamili ... [详细]
  • Electron中使用globalShortcut模块来注册全局快捷键,以实现类似于微信和QQ按快捷键调用剪切窗口的功能。快捷键在应用程序加载完成后进行注册 ... [详细]
  • 史上最全的Websocket入门教程
    websocket是什么?答:它是一种网络通信协议,是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。为什么需要websocket?疑问?我 ... [详细]
  • 【JVM技术专题】深入分析CG管理和原理查缺补漏「番外篇」
    前提概要本文主要针对HotspotVM中“CMSParNew”组合的一些使用场景进行总结。自Sun发布Java语言以来,开始使用GC技术来进行内存自动管理࿰ ... [详细]
  • 问题描述查看matplotlib版本信息的方法总结问题描述在使用matplotlib画violinplot这个图形的时候报错了。出现了下面的错误提示:Attrib ... [详细]
  • #usernobody;worker_processes1;#error_loglogserror.log;#error_loglogserror.lognotice;#error ... [详细]
author-avatar
wan乄etsnyd
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有